﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Spanner;

namespace SpannerWebSite
{
    internal partial class RefTopLevel : Reference
    {
        internal static SpannerChapter TheChapter;

        static RefTopLevel()
        {
            var sections = new List<SpannerSection> { };
            var initActs = new List<Act> { };

            // Write out files we're going to depend on here.  I know, I know, this is
            // very naughty in a static constructor...

            LibraryModelDemo.WriteWebPage();
            new ThirdPartyDemo().WriteWebPage();

            sections.Add(
                Section("Overview", "Overview",
                    FileFragmentFigure(Group(Em("Hello, World!"), " in ", Spanner, " in C#."),
                        Lang.csharp, "../../Intro.HelloWorld.cs", "", null, ignoreUnindentedLines: false
                    ),
                    P("Running this program will create (if they don't already exist) sub-directories ",
                    Code("html"), ", ",
                    Code("css"), ", ",
                    Code("js"), ", and ",
                    Code("media"),
                    " in ", Code(Const.DemoWebSitePath), " and populate them.  By default, ",
                    Code("WriteWebPage"), " will generate a single-file solution, in this case putting ",
                    "all HTML, JavaScript, and so forth in ", Code("html/HelloWorld.html"), ".")
                )
            );

            sections.Add(
                Section("The UI Class", "UIClass",
                    "The ", Code("UI"), " class is where ", Spanner, " exposes the bulk of its API.  By making your ",
                    "own top-level classes sub-classes of ", Code("UI"), " you can avoid all the tedious ",
                    "explicit qualification of ", Spanner, " API methods."
                )
            );

            sections.Add(
                Section("The Html Class", "HtmlClass",
                    "The ", Code("Html"), " class represents HTML elements.  Every HTML element has a ",
                    "correspondingly named ", Spanner, " constructor function.  For example, ",
                    Code("Div(HR(), P(\"Hi!\"), HR())"), " denotes the HTML ",
                    Code("<div><hr><p>Hi!</p><hr></div>"), "."
                )
            );

            sections.Add(
                Section("The Attrs Class", "AttrsClass",
                    "The ", Code("Attrs"), " class represents HTML attributes.  Attributes are added to ",
                    "HTML elements thus: ",
                    Code("Span(\"Hi!\").WithAttr(\"id\", \"xyz\").WithAttr(\"title\", \"Greetings.\")"),
                    " denotes the HTML ", Code("<span id='xyz' title='Greetings.'>Hi!</span>"), ". ",
                    "Various convenience functions are provided such as ", Code("WithID"), " and ",
                    Code("WithClass"), ".  The ", Code("Attrs"), " support is also aware of structured ",
                    "attributes such as ", Code("class"), " and ", Code("style"), " - for example, ",
                    Code("Span(\"Hi!\").WithAttr(\"class\", \"foo\").WithAttr(\"class\", \"bar\")"), " ",
                    "represents the HTML ", Code("<span class=\"foo bar\">Hi!</span>"), "."
                )
            );

            sections.Add(
                Section("The Expr Class", "ExprClass",
                    "The ", Code("Expr<T>"), " class represents JavaScript values of type ", Code("T"), ", ",
                    "including JavaScript functions. ",
                    "You can use most of the standard C# operators where appropriate, with a few exceptions ",
                    "(notably ", Code("&&"), ", ", Code("||"), ", and ", Code("? :"), " for which ", Spanner, " ",
                    "provides alternative syntax). ",
                    "Note that ", Spanner, " expressions largely hide the distinction between ordinary ",
                    "JavaScript variables and ", Knockout, " observables.",
                    CodeFigures("ExprDemo", () => {
                        // ExprDemo
                        var x = Var<int>("x", 1);
                        var y = ObsVar<int>("y", 2);
                        var z = ComputedVar<int>("z", x + y);
                        // END
                        Content = Show(z);
                    }, skipHtml: true, includeTryItLink: false)
                )
            );

            sections.Add(
                Section("Variables: Var, ObsVar, ComputedVar, and Param", "Vars",
                    "Variables can be ordinary JavaScript variables, ", Knockout, " observables, ",
                    Knockout, " 'computeds', or function parameters.  Variables all share the same ",
                    "syntax and, except where a ", Knockout, " variable is required, can be used ",
                    "interchangeably.  The one syntactic fly in the ointment is the need to use ",
                    Code("Set(x, y)"), " to assign variable ", Code("x"), " rather than something ",
                    "more perspicuous, a problem for which we can blame C#'s quirky operator ",
                    "overloading rules."
                )
            );

            sections.Add(
                Section("The Act Class", "ActClass",
                    "The ", Code("Act"), " class represents JavaScript statements.  Virtually all JavaScript ",
                    "statements are represented, although typically with slightly different syntax. ",
                    CodeFigures("ActDemo", () => {
                        // ActDemo
                        var x = Var<int>("x", 1);
                        var y = ObsVar<int>("y", 2);
                        InitAct =
                            IfThen(x < 3, Do(
                                Set(x, 3),
                                Set(y, y + 1)
                            ));
                        // END
                    }, skipHtml: true, includeTryItLink: false)
                )
            );

            sections.Add(
                Section("Functions and Procedures", "FuncsAndProcs",
                    "Being strongly typed, ", Spanner, " draws a distinction between ",
                    Em("functions"), ", which are evaluated to obtain a value, and ",
                    Em("procedures"), ", which are evaluated purely for a side effect. ",
                    "Impure functions (i.e., with side effects) are supported via ",
                    Code("ActExpr"), ".  Good functional programmers will eschew such ",
                    "a practice!",
                    CodeFigures("FnProcDemo", () => {
                        // FnProcDemo
                        var x = Var<int>("x", 123);
                        var isEven = Function<int, bool>("isEven", a => a % 2 == 0);
                        var incX = Procedure<int>("incX", y => Set(x, x + y));
                        var xs = Var<int[]>("xs", Array(1, 2, 3));
                        var ys = Var<int[]>("ys", Array<int>());
                        InitAct = Do(
                            Call(incX, 1),
                            Set(ys, Map(Filter(xs, isEven), Fn<int, int>(b => b + 1)))
                        );
                        // END
                    }, skipHtml: true, includeTryItLink: false)
                )
            );

            sections.Add(
                Section("TemplateModels and LibraryModels", "TemplatesAndLibraries",
                    P("Re-usable views and view models are supported via ", Code("TemplateModel"), " ",
                    "while shared code libraries are supported via ", Code("LibraryModel"), "."),
                    P("The following example demonstrates the top-level document using a ",
                    Em("list of items"), " template which, in turn, uses a ", Em("single item "),
                    "template:"),
                    CodeFigures("TemplateDemo", () => {
                        // TemplateDemo
                        // Templates would typically be defined in separate C# classes;
                        // here we define them in-line for the sake of brevity.

                        // The template and view model for an "item":
                        var item = Var<string>("item", "");
                        var itemTemplate = TemplateModel<string>(
                            "itemTemplate",
                            // The HTML for the template.
                            P().WithAttr("color", "green").KoText(item),
                            // The initialisation procedure for the template.
                            x => Set(item, x)
                            // No dependencies.
                        );

                        // The template and view model for a list of "items"
                        // using the "item" template:
                        var itemInsts = Var<TemplateModel[]>("itemInsts", null);
                        var itemsTemplate = TemplateModel<string[]>(
                            "itemsTemplate",
                            // The HTML for the template.
                            Div().KoForEach(itemInsts, x => Div().KoTemplate(x)),
                            // The initialisation procedure for the template.
                            xs => Set(itemInsts, 
                                Map(xs, Fn<string, TemplateModel>(x => NewTemplateModelInstance(itemTemplate, x)))
                            ),
                            itemTemplate // Dependency (could be many such).
                        );

                        // The top-level view using the "items" template.
                        var names = Array<string>("Alice", "Bob", "Cissy");
                        var itemsInst = Var<TemplateModel>("itemsInst",
                            NewTemplateModelInstance(itemsTemplate, names));
                        Content = Div().KoTemplate(itemsInst);
                        Dependencies = new Model[] { itemsTemplate }; // Top-level dependencies.
                        // END
                    }, startJSFrom: "function itemTemplate"),
                    P("[Note that each ", Code("TemplateModel"), " definition, as well as the top-level page, ",
                    "has to include the list of template models on which it depends.  ", Spanner, " cannot ",
                    "infer these dependencies because template model instances might be passed in from anywhere.]"),
                    P("The next example shows how ", Em("library models"), " are defined and used in ", Spanner),
                    FileFragmentFigure("Handwritten C#",
                        Lang.csharp, "../../RefTopLevel.LibraryModelDemo.cs", "", null,
                        ignoreUnindentedLines: false),
                    FileFragmentFigure(
                        "Generated web page",
                        Lang.markup, Path.Combine(Const.DemoWebSitePath, "html", "LibraryModelDemo.html"), "", null,
                        ignoreUnindentedLines: false,
                        filter: StripSpannerRuntimeFilter
                    ),
                    P(
                        A("[Try it.]")
                            .WithAttr("href", Const.DemoWebSiteRelativePath("html", "LibraryModelDemo.html"))
                    ),
                    P("Note that ", Spanner, " automatically orders dependencies so they are included in the ",
                    "correct order.")
                )
            );

            sections.Add(
                Section("Third party code", "ThirdPartyCode",
                    P(Spanner, " integrates nicely with third-party JavaScript libraries.  Here is an ",
                    "example showing how one might use ", JQuery, " (a real ", JQuery, " binding would ",
                    "obviously be much larger, but this serves to get the idea across)."),
                    FileFragmentFigure("Handwritten C#",
                        Lang.csharp, "../../RefTopLevel.ThirdPartyDemo.cs", "", null,
                        ignoreUnindentedLines: false),
                    P("Observe the use of ", Code("ExternalNonSpannerLibraryModel"), " to include ", JQuery, " ",
                    "by reference to its URL. (There is a similar ", Code("NonSpannerLibraryModel"), " method ",
                    "for directly specifying the JavaScript code for a third-party library.)"),
                    P("Obserbe also the use of ", Code("JSExpr"), " to generate in-line JavaScript ",
                    "which integrates neatly with ", Spanner, " values. (There is a similar ", Code("JSAct"), " ",
                    "method for specifying in-line actions -- that is, for code which does not return a value.)"),
                    FileFragmentFigure(
                        "Generated web page",
                        Lang.markup, Path.Combine(Const.DemoWebSitePath, "html", "ThirdPartyDemo.html"), "", null,
                        ignoreUnindentedLines: false,
                        filter: StripSpannerRuntimeFilter
                    ),
                    P(
                        A("[Try it.]")
                            .WithAttr("href", Const.DemoWebSiteRelativePath("html", "ThirdPartyDemo.html"))
                    ),
                    P("As mentioned, ", Spanner, " automatically orders dependencies so they are included in the ",
                    "correct order.")
                )
            );

            sections.Add(
                Section("POCOs", "POCOs",
                    P(Spanner, " makes it easy to integrate with standard POCOs.  By including the T4 ",
                    "template file, ", Code("SpannerPocos.tt"), " in your Visual Studio solution, extension ",
                    "methods will be generated for all public, top-level classes in your solution with the ",
                    "annotation ", Code("[SpannerPoco]"), ".  For example, the ", Em("Memory Game"), " from ",
                    "the ", A("home page").WithAttr("href", "#Home-ANonTrivialExample"), " features the following ",
                    "POCO:"),
                    FileFragmentFigure(Span("POCO from the ", Em("Memory Game"), " demo."),
                        Lang.csharp, "../../Home.MemoryGame.cs", "[SpannerPoco]", "class MemoryGame"
                    ),
                    P("Normally, for a variable ", Code("x"), " of type ", Code("Expr<Tile>"), " we would be ",
                    "forced to access its fields via the cumbersome construction ", Code("x.Field(o => o.Index)"), ", ",
                    "et cetera.  Again, this awkwardness is because C# is not well suited to embedded domain ",
                    "specific languages. However, with ", Code("SpannerPocos.tt"), " in the solution, the ",
                    "extension methods below will be generated, allowing us to access the fields of ",
                    Code("x"), " via the more agreeable syntax of ", Code("x.Index()"), ", et cetera. ",
                    "This syntax even works for assignments, such as ",
                    Code("Set(x.Index(), 42)"), ".  Not perfect, but much better than the alternative."),
                    FileFragmentFigure(Span("Extension methods generated by ", Code("SpannerPocos.tt")),
                        Lang.csharp, "../../SpannerPocos.cs", "namespace Spanner", null
                    ),
                    P("Observe that you can include ", Code("ObsVar"), " fields in your POCOs, which will ",
                    "be treated slightly differently so as to give the same semantics as ordinary ",
                    Code("ObsVar"), "s in the rest of your code.  Note that, currently, nested classes, ",
                    "properties, methods, and inheritance are not supported by ", Code("SpannerPocos.tt"), ". ")
                )
            );

            TheChapter = CommonUI.Chapter("Reference - Top Level", "RefTopLevel", sections, initActs);
        }
    }
}
